/*
 * 础光实时操作系统PhotonRTOS -- 系统打印
 *
 * Copyright (C) 2022, 2023 国科础石(重庆)软件有限公司
 *
 * 作者: Baoyou Xie <xiebaoyou@kernelsoft.com>
 *
 * License terms: GNU General Public License (GPL) version 3
 *
 */

#include <photon/console.h>
#include <photon/irq.h>
#include <photon/smp.h>

#define default_message_loglevel 4
/**
 * 所有控制台设备的全局链表
 */
static struct double_list console_devices =
	LIST_HEAD_INITIALIZER(console_devices);

/**
 * 控制台锁
 */
static struct smp_lock cons_lock =
			SMP_LOCK_UNLOCKED(cons_lock);
extern struct simple_console global_simple_console;

/**
 * 系统日志缓冲区
 * 系统日志环形缓冲区头尾标志：往tail指针处写数据，head指针读数据
 * 系统日志只存放默认日志等级之上的日志，
 * 用户可以调用接口将日志打印到控制台，若缓冲区不空时，
 * 将log_head到log_tail之间的内容全部打印，然后清空缓冲区
*/
#define LOG_BUF_LEN (1 << CONFIG_LOG_BUF_SHIFT)
#define LOG_BUF_MASK (LOG_BUF_LEN - 1)
#define LOG_BUF(idx) (log_buf[(idx) & LOG_BUF_MASK])
static char log_buf[LOG_BUF_LEN];
static uintptr_t log_head;
static uintptr_t log_tail;

/**
 * 保存格式化消息的缓冲区
 */
static char format_buf[1024];


/**
 * 拷贝系统日志缓冲区
 * 输入：拷贝的目标缓冲区 buf
 * 	拷贝的大小 size
 * 	标志位，是否清空缓冲区，buf_clear  true为是，false为否
 * 输出：拷贝的字符个数
*/
uint32_t dmesg(char buf[], uint32_t size, bool buf_clear)
{
	uintptr_t start, end;
	buf[size--] = '\0';
	if (log_head == log_tail) {
		printk("系统日志缓冲区为空！\n");
		return 0;
	}
	end = log_tail;
	if (size >= LOG_BUF_LEN) {
		start = log_head;
	} else if (size <= 0) {
		pr_err("size不合法!\n");
		return 0;
	} else {
		/**
		 * 此时buf的size是合法的，根据size调整需要拷贝的区域指针
		*/
		if (end > log_head) {
			/**
			 * 缓冲区没有回绕
			*/
			if (end - log_head >= size) {
				/**
				 * 缓冲区中的内容大于等于需要拷贝的size
				*/
				start = end - size;
			} else {
				start = log_head;
			}
		} else {
			/**
			 * 缓冲区回绕了，那么只用拷贝log_tail往前size个字符
			*/
			if (size < end) {
				start = end - size;
			} else {
				start = LOG_BUF_LEN - size + end;
			}
		}
	}
	if (buf) {
		if (end > start) {
			memcpy((void*)buf, &LOG_BUF(start), end - start);
		} else {
			/**
			 * 缓冲区回绕了
			*/
			memcpy((void*)buf, &LOG_BUF(start), LOG_BUF_LEN - start);
			memcpy((void*)(buf + LOG_BUF_LEN - start), &LOG_BUF(0), end);
		}
		
	} else {
		pr_err("需要输入一个缓冲区！\n");
		return 0;
	}

	/**
	 * 清空缓冲区，也可以选择不清空
	*/
	if (buf_clear) {
		log_head = log_tail = 0;
		memset(log_buf, 0, sizeof(log_buf));
	}
	return end > start ? end - start : LOG_BUF_LEN - start + end;
}

/**
 * 将默认日志等级以上的日志存入系统日志缓冲区中，
 * 并不会打印到控制台，除非用户调用相应接口。
*/
static void advance_log_buf(char ch) {
	/**
	 * 存入缓冲区时，先判断缓冲区是否已满
	 * 环形缓冲区，如果缓冲区满了，直接覆盖前面的内容
	*/
	if (log_tail == ((log_head - 1) & LOG_BUF_MASK)) {
		log_head++;
	}
	LOG_BUF(log_tail) = ch;
	log_tail++;
	log_tail &= LOG_BUF_MASK;
	log_head &= LOG_BUF_MASK;
	/**
	 * 对比msg_buf，这里没有write_pos，因为每次都是
	 * 打印整个缓冲区
	*/
}

/**
 * 精简版内核打印，无消息缓冲，支持日志等级
 */
int32_t printk(const char *fmt, ...)
{
	uintptr_t flags;
	char *p;
	va_list args;
	int32_t len;
	int32_t loglevel;

	va_start(args, fmt);

	smp_lock_irqsave(&cons_lock, flags);

	memset(format_buf, 0, sizeof(format_buf));
	/**
	 * 将当前字符输出到临时缓冲区
	 */
	len = vsprintf(format_buf, fmt, args);

	/* 检查消息等级，没有则使用默认等级进行打印 */
	p = format_buf;

	if (p[0] == '<' && p[1] >= '0' && p[1] <= '7' && p[2] == '>') {
		loglevel = p[1] - '0';
		p += 3; /* point to msg */
	} else {
		loglevel = default_message_loglevel;
	}

	if (loglevel <= KERN_LOGLEVEL) {
		global_simple_console.write(p, len);
	}
	/**
	 * 只将日志等级高于默认日志等级的消息放入系统日志缓冲区
	*/
	if (loglevel < default_message_loglevel) {
		advance_log_buf(*p);
	}

	smp_unlock_irqrestore(&cons_lock, flags);

	va_end(args);

	return len;
}